﻿//////////////////////////////////////////////////////
// 程序名称：实验题0-1、0-3、0-4、0-5
// 功能：各个题目、金刚石（一笔绘制，n可以取任意素数/奇数，n=15 r=300通过测试）
// 编译环境：Visual Studio 2017，EasyX 2018春分版
// 作者：唐瑞宏 <2018301010303> <2044291597@qq.com>
// 最后修改：2020-3-4 暂时不用头文件

#include <iostream>
#include <graphics.h>
#include <conio.h>
#include <math.h>
#include <vector>
#include <Windows.h>
using namespace std;
//可以显示划线的过程，bold可以设定线条粗细，speed设置速度(越大越慢)
void show_lineto2(int x2, int y2, double bold, int speed) {
	double x1 = getx(), y1 = gety();
	double dx = x2 - x1, dy = y2 - y1;
	double s = sqrt(dx * dx + dy * dy);
	double sina = dy / s, cosa = dx / s;
	while (round(x1) != x2 || round(y1) != y2) {
		x1 += cosa;
		y1 += sina;
		solidcircle(round(x1), round(y1), ceil(bold/2));
		Sleep(speed);
	}
}
//可以直接画一系列的线，第一个点会被作为开始点
void series_line(vector<int> points, double bold, int speed) {
	moveto(*(points.begin()), *(points.begin() + 1));
	for (auto it = points.begin() + 2;it != points.end();it += 2) {
		show_lineto2(*it, *(it + 1), bold, speed);
		moveto(*it, *(it + 1));
	}
}
void ans0() {
	initgraph(700, 300);
	setbkcolor(WHITE);
	setlinecolor(BLACK);
	settextcolor(BLACK);
	cleardevice();
	setorigin(30, 20);
	setfillcolor(BLACK);
	vector<int> point1{ 30,20,30,100,70,100,70,60,30,60 };
	series_line(point1, 2, 10);
	TCHAR s[] = _T("第二幅图无法一笔画     第三幅图无法一笔画");
	outtextxy(80, 20, s);
	vector<int> point4{ 445,20,445,100,485,100,445,60,485,60,485,100 };
	series_line(point4, 2, 10);
}
//***************
void pause_lineto(int x, int y) {
	system("pause");
	lineto(x, y);
}
//求最大公约数
int gcd(int a, int b) {
	if (a < b) { int t = a;a = b;b = t; }
	while (b) {
		a = a % b;
		int t = a;a = b;b = t;
	}
	return a;
}
//将相加值、循环值和被操作数输入 将输出下一个offset(0123...)
int nextOffset(int add, int circle, int num) {
	num += add;
	if (num >= circle) {
		num -= circle;
	}
	return num;
}
//将画边缺失的线段标示在flag里
vector<int> findNums(int n) {
	vector<int> flag;
	int s = 0, max = 0;
	for (size_t i = 2; i <= n/2; i++)
	{
		if (gcd(i, n) != 1) {
			flag.push_back(i);
			flag.push_back(gcd(i, n));
			if (max < *(flag.end() - 1)) { max = *(flag.end() - 1); }
		}
	}
	flag.push_back(max);
	return flag;
}
//输出点，并将点的坐标存在vector<int>xy中
vector<int> findPoints(int n, int r) {
	vector<int>xy;
	double pai = 3.1416;
	setfillcolor(RED);
	for (double i = 0;i <= 2 * pai;i += 2 * pai / n) {
		double x = r * sin(i), y = r * cos(i);
		xy.push_back(x);
		xy.push_back(y);
		solidcircle(x, y, 2);
	}
	if (!*(xy.end() - 2)) { xy.erase(xy.end() - 2, xy.end()); }//因为当x回到0时，i总小于表达式，因此需删掉那一个点 ps.但是当n=33时会报错，因此加入判断
	return xy;
}
//只要没到达初始点，就一直按照间隔数划线，refer/space请以点为单位输入(请*2)
void loop(int refer, int space, vector<int> points, int n) {
	int offset = refer, toX = 0, toY = 0;
	while (true) {
		offset = nextOffset(space, n, offset);
		toX = *(points.begin() + offset);
		toY = *(points.begin() + 1 + offset);
		pause_lineto(toX, toY);
		if (offset == refer) { return; }
	}
}
void diamond(int n,int r) {
	vector<int> nums = findNums(n);//特殊的数，每组两个，分别表示(间隔数, 周期数(即，gcd值))
	vector<int> points = findPoints(n, r);//需要连的点
	moveto(points[0], points[1]);//初始点
	int offset = 0, toX = 0, toY = 0;
	for (size_t i = 2; i <= n/2; i++)//i为起始点与目标点中间的边数，简单划线
	{
		loop(0, 2 * i, points, 2 * n);
	}
	int numsEnd = *(nums.end() - 1);
	nums.pop_back();
	for (size_t j = 0, refer = 2; j < numsEnd; j++, refer += 2)//控制改变refer点
	{
		toX = *(points.begin() + refer);
		toY = *(points.begin() + 1 + refer);
		pause_lineto(toX, toY);
		for (auto it = nums.begin();it != nums.end();it += 2) {
			if (*(it+1) > refer / 2) {//开始画圈
				loop(refer, *it * 2, points, 2 * n);// 这是loop埋下的坑
			}
		}
		offset = refer;
	}	
	for (auto it = points.begin() + offset + 2;it != points.end();it += 2) {//最后连一圈
		toX = *it;
		toY = *(it + 1);
		pause_lineto(toX, toY);
	}
	pause_lineto(*points.begin(), *(points.begin() + 1));
}
void ans1() {
	int n, r;
	cout << "在绘图框出现时，按任意键划线\n请依次输入边数n及半径r\n";
	cin >> n >> r;
	if (n % 2) {
		initgraph(2 * r + 200, 2 * r + 200);
		setbkcolor(WHITE);
		setlinecolor(BLACK);
		cleardevice();
		setorigin(r + 100, r + 100);
		diamond(n, r);
	}
	else {
		cout << "不能一笔画出";
	}
}
//***************
void ans2() {
	int point1[] = { 100,0,128,0,48,80,150,80,170,100,0,100,100,0 };
	int point2[] = { 0,100,20,120,220,120,128,28,113,43,170,100,0,100 };
	int point3[] = { 76,80,128,28,220,120,228,100,128,0,48,80,76,80 };
	initgraph(700, 600);
	setbkcolor(WHITE);
	setlinecolor(RGB(168,0,0));
	cleardevice();
	setfillcolor(RED);
	fillpolygon((POINT*)point1, 6);
	setfillcolor(YELLOW);
	fillpolygon((POINT*)point2, 6);
	setfillcolor(BLUE);
	fillpolygon((POINT*)point3, 6);
}
//***************
void ans3() {
	initgraph(700, 600);
	setbkcolor(WHITE);
	setlinecolor(BLACK);
	cleardevice();
	MOUSEMSG mouse;
	int flag = 1, x1, x2, y1, y2;
	while (true) {
		mouse = GetMouseMsg();
		if (mouse.uMsg == WM_LBUTTONDOWN) {
			x1 = mouse.x;
			y1 = mouse.y;
		}
		if (mouse.uMsg == WM_RBUTTONDOWN) {
			line(x1, y1, mouse.x, mouse.y);
		}
		if (mouse.uMsg == WM_MBUTTONDOWN) {
			break;
		}
	}
}
int main()
{	
	cout << "这是一个包含4个题目的程序，各个题目按照作业中顺序依次出现，在题目完成后，按任意键继续查看下一题\n";
	system("pause");
	system("cls");
	ans0();
	system("pause");
	closegraph();
	ans1();
	system("pause");
	closegraph();
	ans2();
	system("pause");
	closegraph();
	system("cls");
	cout << "下一道题，按鼠标中键退出\n";
	system("pause");
	system("cls");
	ans3();
	closegraph();
	cout << "题目展示完成\n";
	system("pause");
	return 0;
}